using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace MicroFractal.iTunesBrowser.Core
{
    internal class MobileDevice
    {
        // Fields
        private const string DLLPath = "iTunesMobileDevice.dll";

        // Methods
        static MobileDevice()
        {
            string lpValue = Environment.GetEnvironmentVariable("Path") + ";" + Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles) + @"\Apple\Mobile Device Support\bin";
            SetEnvironmentVariable("Path", lpValue);
        }

        public static int AFCConnectionOpen(IntPtr handle, uint io_timeout, ref afc_connection conn)
        {
            IntPtr zero = IntPtr.Zero;
            int num = AFCConnectionOpen(handle, io_timeout, ref zero);
            if ((num == 0) && (zero != IntPtr.Zero))
            {
                conn = (afc_connection)Marshal.PtrToStructure(zero, ((afc_connection)conn).GetType());
            }
            return num;
        }

        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCConnectionOpen(IntPtr handle, uint io_timeout, ref IntPtr conn);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCDirectoryClose(IntPtr conn, IntPtr dir);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCDirectoryCreate(IntPtr conn, string path);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCDirectoryOpen(IntPtr conn, string path, ref IntPtr dir);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCDirectoryRead(IntPtr conn, IntPtr dir, ref IntPtr dirent);
        public static int AFCDirectoryRead(IntPtr conn, IntPtr dir, ref string buffer)
        {
            IntPtr zero = IntPtr.Zero;
            int num = AFCDirectoryRead(conn, dir, ref zero);
            if ((num == 0) && (zero != IntPtr.Zero))
            {
                buffer = Marshal.PtrToStringAnsi(zero);
                return num;
            }
            buffer = null;
            return num;
        }

        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCFileRefClose(IntPtr conn, long handle);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCFileRefOpen(IntPtr conn, string path, int mode, int unknown, out long handle);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCFileRefRead(IntPtr conn, long handle, byte[] buffer, ref uint len);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCFileRefSeek(IntPtr conn, long handle, uint pos, uint origin);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCFileRefSetFileSize(IntPtr conn, long handle, uint size);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCFileRefTell(IntPtr conn, long handle, ref uint position);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCFileRefWrite(IntPtr conn, long handle, byte[] buffer, uint len);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCFlushData(IntPtr conn, long handle);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCGetFileInfo(IntPtr conn, string path, ref IntPtr buffer, out uint length);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCRemovePath(IntPtr conn, string path);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFCRenamePath(IntPtr conn, string old_path, string new_path);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AMDeviceConnect(ref AMDevice device);
        public static string AMDeviceCopyValue(ref AMDevice device, uint unknown, string name)
        {
            byte[] cfstring = StringToCFString(name);
            IntPtr ptr = AMDeviceCopyValue_Int(ref device, unknown, cfstring);
            if (ptr != IntPtr.Zero)
            {
                byte len = Marshal.ReadByte(ptr, 8);
                if (len > 0)
                {
                    return Marshal.PtrToStringAnsi(new IntPtr(ptr.ToInt64() + 9L), len);
                }
            }
            return string.Empty;
        }

        [DllImport("iTunesMobileDevice.dll", EntryPoint = "AMDeviceCopyValue", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr AMDeviceCopyValue_Int(ref AMDevice device, uint unknown, byte[] cfstring);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AMDeviceGetConnectionID(ref AMDevice device);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AMDeviceIsPaired(ref AMDevice device);
        public static int AMDeviceNotificationSubscribe(DeviceNotificationCallback callback, uint unused1, uint unused2, uint unused3, ref AMDeviceNotification notification)
        {
            IntPtr zero = IntPtr.Zero;
            int num = AMDeviceNotificationSubscribe(callback, unused1, unused2, unused3, ref zero);
            if ((num == 0) && (zero != IntPtr.Zero))
            {
                notification = (AMDeviceNotification)Marshal.PtrToStructure(zero, ((AMDeviceNotification)notification).GetType());
            }
            return num;
        }

        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern int AMDeviceNotificationSubscribe(DeviceNotificationCallback callback, uint unused1, uint unused2, uint unused3, ref IntPtr am_device_notification_ptr);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AMDeviceStartService(ref AMDevice device, byte[] service_name, ref IntPtr handle, IntPtr unknown);
        public static int AMDeviceStartService(ref AMDevice device, string service_name, ref afc_connection conn, IntPtr unknown)
        {
            IntPtr zero = IntPtr.Zero;
            int num = AMDeviceStartService(ref device, StringToCFString(service_name), ref zero, unknown);
            if ((num == 0) && (zero != IntPtr.Zero))
            {
                conn = (afc_connection)Marshal.PtrToStructure(zero, ((afc_connection)conn).GetType());
            }
            return num;
        }

        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AMDeviceStartSession(ref AMDevice device);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AMDeviceValidatePairing(ref AMDevice device);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AMRestoreModeDeviceCreate(uint unknown0, int connection_id, uint unknown1);
        [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AMRestoreRegisterForDeviceNotifications(DeviceRestoreNotificationCallback dfu_connect, DeviceRestoreNotificationCallback recovery_connect, DeviceRestoreNotificationCallback dfu_disconnect, DeviceRestoreNotificationCallback recovery_disconnect, uint unknown0, IntPtr user_info);
        internal static string CFStringToString(byte[] value)
        {
            return Encoding.ASCII.GetString(value, 9, value[9]);
        }

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool SetEnvironmentVariable(string lpName, string lpValue);
        internal static byte[] StringToCFString(string value)
        {
            byte[] bytes = new byte[value.Length + 10];
            bytes[4] = 140;
            bytes[5] = 7;
            bytes[6] = 1;
            bytes[8] = (byte)value.Length;
            Encoding.ASCII.GetBytes(value, 0, value.Length, bytes, 9);
            return bytes;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct afc_connection
    {
        private uint handle;
        private uint unknown0;
        private byte unknown1;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        private byte[] padding;
        private uint unknown2;
        private uint unknown3;
        private uint unknown4;
        private uint fs_block_size;
        private uint sock_block_size;
        private uint io_timeout;
        private IntPtr afc_lock;
        private uint context;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct afc_directory
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0)]
        private byte[] unknown;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct AMDevice
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
        internal byte[] unknown0;
        internal uint device_id;
        internal uint product_id;
        public string serial;
        internal uint unknown1;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        internal byte[] unknown2;
        internal uint lockdown_conn;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        internal byte[] unknown3;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct AMDeviceNotificationCallbackInfo
    {
        internal IntPtr dev_ptr;
        public NotificationMessage msg;
        public AMDevice dev
        {
            get
            {
                return (AMDevice)Marshal.PtrToStructure(this.dev_ptr, typeof(AMDevice));
            }
        }
    }

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    internal delegate void DeviceNotificationCallback(ref AMDeviceNotificationCallbackInfo callback_info);

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct AMDeviceNotification
    {
        private uint unknown0;
        private uint unknown1;
        private uint unknown2;
        private DeviceNotificationCallback callback;
        private uint unknown3;
    }

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    internal delegate void DeviceRestoreNotificationCallback(ref AMRecoveryDevice callback_info);

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct AMRecoveryDevice
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] unknown0;
        public DeviceRestoreNotificationCallback callback;
        public IntPtr user_info;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
        public byte[] unknown1;
        public uint readwrite_pipe;
        public byte read_pipe;
        public byte write_ctrl_pipe;
        public byte read_unknown_pipe;
        public byte write_file_pipe;
        public byte write_input_pipe;
    }

    public enum NotificationMessage
    {
        Connected = 1,
        Disconnected = 2,
        Unknown = 3
    }

}
